use super::{TypeSchema, ProtocolError};
use lazy_static::lazy_static;
use std::fs;
use std::path::Path;
use serde_json::Value;
use std::collections::{HashMap, HashSet};
use std::time::Instant;

pub struct DataType {
    types: HashMap<String, TypeSchema>,
}

impl DataType {
    pub fn new() -> Result<Self, ProtocolError> {
        let json_path = Path::new("datatypes.json");
        let json_content = fs::read_to_string(json_path)
            .map_err(|e| ProtocolError::DataTypeError(format!("Failed to read datatypes.json: {}", e)))?;
        let json_array: Vec<Value> = serde_json::from_str(&json_content)
            .map_err(|e| ProtocolError::DataTypeError(format!("Failed to parse datatypes.json: {}", e)))?;

        let mut types = HashMap::new();
        let mut type_names = HashSet::new();
        for item in &json_array {
            let name = item["name"].as_str()
                .ok_or_else(|| ProtocolError::DataTypeError("Missing 'name' field".to_string()))?;
            let def = item["def"].as_str().map(|x| TypeSchema::parse(x))
                .or_else(|| item["short"].as_str().map(|x| TypeSchema::parse_short(x)))
                .ok_or_else(|| ProtocolError::DataTypeError("Missing both 'def' and 'short' fields".to_string()))?;
            types.insert(name.to_string(), def);
            type_names.insert(name.to_string());
        }

        let mut dependencies: HashMap<String, HashSet<String>> = HashMap::new();
        for (name, schema) in &types {
            let deps = Self::extract_dependencies(schema);
            dependencies.insert(name.clone(), deps);
        }

        for (name, deps) in &dependencies {
            for dep in deps {
                if !type_names.contains(dep) {
                    return Err(ProtocolError::DataTypeError(
                        format!("Type '{}' depends on undefined type '{}'", name, dep)
                    ));
                }
            }
        }

        if let Some(cycle) = Self::detect_cycle(&dependencies) {
            return Err(ProtocolError::DataTypeError(
                format!("Circular dependency detected: {}", cycle.join(" -> "))
            ));
        }

        Ok(Self { types })
    }

    fn extract_dependencies(schema: &TypeSchema) -> HashSet<String> {
        let mut deps = HashSet::new();
        match schema {
            TypeSchema::Void | TypeSchema::Int | TypeSchema::Float | TypeSchema::Bool | TypeSchema::Str | TypeSchema::Json => {},
            TypeSchema::List(item) => {
                deps.extend(Self::extract_dependencies(item));
            },
            TypeSchema::Map(k, v) => {
                deps.extend(Self::extract_dependencies(k));
                deps.extend(Self::extract_dependencies(v));
            }
            TypeSchema::Tuple(items) => {
                for item in items {
                    deps.extend(Self::extract_dependencies(item));
                }
            },
            TypeSchema::Opt(item) => {
                deps.extend(Self::extract_dependencies(item));
            },
            TypeSchema::DataType(name) => {
                deps.insert(name.clone());
            },
        }
        deps
    }

    fn detect_cycle(dependencies: &HashMap<String, HashSet<String>>) -> Option<Vec<String>> {
        let start = Instant::now();
        let mut visited = HashSet::new();
        let mut path = Vec::new();
        
        fn dfs(
            current: &str,
            dependencies: &HashMap<String, HashSet<String>>,
            visited: &mut HashSet<String>,
            path: &mut Vec<String>,
        ) -> Option<Vec<String>> {
            if path.contains(&current.to_string()) {
                let cycle_start = path.iter().position(|x| x == current).unwrap();
                return Some(path[cycle_start..].to_vec());
            }
            
            if visited.contains(current) {
                return None;
            }
            
            visited.insert(current.to_string());
            path.push(current.to_string());
            
            if let Some(deps) = dependencies.get(current) {
                for dep in deps {
                    if let Some(cycle) = dfs(dep, dependencies, visited, path) {
                        return Some(cycle);
                    }
                }
            }
            
            path.pop();
            None
        }
        
        for node in dependencies.keys() {
            if !visited.contains(node) {
                if let Some(cycle) = dfs(node, dependencies, &mut visited, &mut path) {
                    let duration = start.elapsed();
                    println!("Cycle detection took: {:?}", duration);
                    return Some(cycle);
                }
            }
        }
        
        let duration = start.elapsed();
        println!("Cycle detection took: {:?}", duration);
        None
    }

    pub fn explain(&self, name: &str) -> Option<&TypeSchema> {
        self.types.get(name)
    }
}

lazy_static! {
    pub static ref SYSTEM_DATA_TYPE: DataType = DataType::new().unwrap();
}
